package JUC;

import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class ABADemo {
    /**
     * 演示ABA问题的产生与解决方法
     * <p>
     * 本程序通过两个示例分别演示了：
     * 1. ABA问题的产生：使用AtomicReference在多线程环境下可能出现ABA问题
     * 2. ABA问题的解决：使用AtomicStampedReference通过版本戳机制避免ABA问题
     *
     * @param args 命令行参数数组
     * @throws InterruptedException 当线程睡眠被中断时抛出此异常
     */
    // ABA问题的解决
    public static void main(String[] args) throws InterruptedException {
        System.out.println("==========ABA问题的产生=========");
        AtomicReference atomicReference = new AtomicReference(100);

        // 线程AAA执行CAS操作：先将100改为101，再将101改回100
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() +
                    "\t" + atomicReference.compareAndSet(100, 101) +
                    "\t" + atomicReference.get());
            System.out.println(Thread.currentThread().getName() +
                    "\t" + atomicReference.compareAndSet(101, 100) +
                    "\t" + atomicReference.get());
        }, "AAA").start();

        // 线程BBB执行CAS操作：尝试将100改为101
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() +
                    "\t" + atomicReference.compareAndSet(100, 101) +
                    "\t" + atomicReference.get());
        }, "BBB").start();

        Thread.sleep(3000);

        System.out.println("==========ABA问题的解决=========");
        int stamp = 0;
        AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100, stamp);

        // 线程CCC执行带版本戳的CAS操作：先将100改为101（版本戳+1），再将101改回100（版本戳+2）
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() +
                    "\t" + atomicStampedReference.compareAndSet(100, 101, stamp, stamp + 1) +
                    "\t当前的实际版本号：" + atomicStampedReference.getStamp());
            System.out.println(Thread.currentThread().getName() +
                    "\t" + atomicStampedReference.compareAndSet(101, 100, stamp + 1, stamp + 2) +
                    "\t当前的实际版本号：" + atomicStampedReference.getStamp());
        }, "CCC").start();

        // 线程DDD执行带版本戳的CAS操作：尝试将100改为101（版本戳+1）
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() +
                    "\t" + atomicStampedReference.compareAndSet(100, 101, stamp, stamp + 1) +
                    "\t当前的实际版本号：" + atomicStampedReference.getStamp());
        }, "DDD").start();
    }

}
